## -*- coding: utf-8 -*-
#'''
#Created on 12/giu/2009
#
#@author: sax
#'''


from django.core.urlresolvers import reverse, resolve, NoReverseMatch
from django.utils.functional import update_wrapper
from django.http import HttpResponse, HttpResponseServerError
from django.db import models
from django.utils.translation import ugettext as _
from django.contrib import  admin
from django.contrib.admin import widgets
from django.contrib.admin.options import get_ul_class
from xadmin.ajax.service import AjaxService
from xadmin.widgets import ReadOnlyWidget, ModelLinkWidget, URLWidget
import xadmin.filterspecs

def get_view_permission(self):
    return 'view_%s' % self.object_name.lower()

__all__ = ['XModelAdmin']

class XModelAdminButton(object):
    def __init__(self, label, link, css=""):
        self.label = label
        self.link = link
        self.css = css # is a css style 

    def __repr__(self):
        return u'<a href="%s" class="buttonlink">%s</a>' % (self.link, self.label)

XFORMFIELD_FOR_DBFIELD_DEFAULTS = {
                                   models.URLField:    {'widget': URLWidget},
                                   }

class XModelAdmin(admin.ModelAdmin):
    disabled = []
    read_only = []
    model_link = []
    details = []
    buttons = []
    services = []
    fk_search_on =  None    
    search_fields_labels = None
    enable_export_csv_url = False
    verbose_name_plural = None

    _MODE_CHANGE = 1
    _MODE_ADD = 2
    _MODE_LIST = 3
    _MODE_UNKNOW = 4
    
    def __init__(self, model, admin_site):
        super(XModelAdmin, self).__init__(model, admin_site)
        
        self.formfield_overrides.update( XFORMFIELD_FOR_DBFIELD_DEFAULTS )
        
        self.__mode = XModelAdmin._MODE_UNKNOW
        self.__add = False
        self.__change = False
        self._services = {} # dict of ajax service: fieldname: AjaxService
        self.fk_service = None
        if self.verbose_name_plural is None:
            self.verbose_name_plural = self.model._meta.verbose_name_plural
                    
        #self.change_list_template = self.change_list_template or self.def_change_list_template
        if bool(self.fk_search_on):
            ret_fields = ['id']
            criteria = []
            for f in self.fk_search_on:
                ret_fields.append( f )
                criteria.append('%s__istartswith' % f )
                
            self.fk_service = '%s_%s_service_fk' % (self.model._meta.app_label, self.model._meta.module_name)
            self._services['fk'] = (self.fk_service, 
                                     AjaxService(self.model.objects.all(), ret_fields, *criteria)
                                     )
            
    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        """
        Get a form Field for a ForeignKey.
        """
        from xadmin.ajax.widgets.autocomplete import AjaxLookupInput

        fk_model_admin = self.admin_site._registry.get(db_field.rel.to)
         # if foreign key is registered in admin and is XModelAdmin
        if fk_model_admin and hasattr(fk_model_admin, 'fk_service'):
            if fk_model_admin.fk_service:
                view_name = "%s:%s" % (self.admin_site.name,fk_model_admin.fk_service) 
                kwargs['widget'] =  AjaxLookupInput( view_name )
                
        return db_field.formfield(**kwargs)
    
    def _get_model_change_link(self, model_instance):
        info = self.admin_site.name, model_instance._meta.app_label, model_instance._meta.module_name #IGNORE:W0212
        name = '%s:%s_%s_change' % info
        return reverse(name, args=[model_instance.pk])

    def _get_model_change_url(self, model_instance, label=None):
        lbl = label or str(model_instance)
        try:
            url = self._get_model_change_link(model_instance)
            ret = '<a class="modellink" href="%s">%s</a>' % (url, lbl)
        except:
            ret = lbl
        return ret

    def _make_readonly(self, form, obj):
        for field_name in self.read_only:
            if field_name in form.base_fields:
                form.base_fields[field_name].widget = ReadOnlyWidget(self.admin_site, obj, field_name)

        for field_name in self.model_link:
            if field_name in form.base_fields:
                form.base_fields[field_name].widget = ModelLinkWidget(self.admin_site, obj, field_name)

        for field_name in self.disabled:
            if field_name in form.base_fields:
                form.base_fields[field_name].widget.attrs['disabled'] = 'disabled'

    def get_form(self, request, obj=None, **kwargs): #IGNORE:W0613
        form = super(XModelAdmin, self).get_form(request, obj)
        if self.__mode ==  XModelAdmin._MODE_CHANGE:
            self._make_readonly(form, obj)
        return form
 
    def change_view(self, request, object_id, extra_context=None):
        self.__mode = XModelAdmin._MODE_CHANGE
        return super(XModelAdmin, self).change_view(request, object_id, extra_context)

    def add_view(self, request, form_url='', extra_context=None):
        self.__mode = XModelAdmin._MODE_ADD
        return super(XModelAdmin, self).add_view(request, form_url, extra_context)
 
    def _get_button_for_view(self, view_name, request): #IGNORE:W0613
        url = reverse("%s:%s" % (self.admin_site.name, view_name))
        handler = resolve(url)
        label = getattr(handler[0], 'label', handler[0].__name__)
        css = getattr(handler[0], 'css', "")
        return XModelAdminButton(label, url, css)

    def get_search_fields_label(self):
        return self.search_fields_labels
        if self.search_fields_labels is None:
            search_fields_labels = []
            for f in self.search_fields:
                f = f.rsplit("__")[0]
                search_fields_labels.append(str(self.model._meta.get_field_by_name(f)[0].verbose_name))
                
        return ", ".join(search_fields_labels)
    
    def get_extra_buttons(self, request):
        btns = []
        for btn in self.buttons:
            if callable(btn):
                btns.append(btn(self, request))
            elif isinstance(btn, basestring):
                try:
                    btns.append(self._get_button_for_view(btn, request))
                except NoReverseMatch:
                    btns.append(btn)
            else:
                btns.append(unicode(btn))
        return btns
                    
    def changelist_view(self, request, extra_context=None):
        extra_context = extra_context or {}
        extra_context['buttons'] = self.get_extra_buttons(request)
        extra_context['search_fields_labels'] = self.get_search_fields_label() 
        return super(XModelAdmin, self).changelist_view(request, extra_context)

    def _setup_services(self):
        for s in self.services:
            info = self.model._meta.app_label, self.model._meta.module_name, s
            if isinstance(s, basestring):
                self._services[s] = ('%s_%s_service_%s' % info, 
                                     AjaxService(self.model.objects.all(), ['id',s], '%s__istartswith' % s , required=['q'])
                                     )
    def __wrap(self, view):
        def wrapper(*args, **kwargs):
            return self.admin_site.admin_view(view)(*args, **kwargs)
        return update_wrapper(wrapper, view)
                
    def get_services_urls(self):
        from django.conf.urls.defaults import patterns, url
        ret = []
        info = self.model._meta.app_label, self.model._meta.module_name
        self._setup_services()
        for fieldname, service in self._services.items():
            print fieldname, service, 'service/%s/$' % fieldname
            ret += patterns('',
                            url(r'^service/%s/$' % fieldname,
                                self.__wrap(service[1]),
                                name=service[0] ),
                        )
        return ret
    
    def get_urls(self):
        from django.conf.urls.defaults import patterns, url
        info = self.model._meta.app_label, self.model._meta.module_name
        
        ret = []
        ret += self.get_services_urls()
        ret += super(XModelAdmin, self).get_urls()
        if self.enable_export_csv_url:
            ret += patterns('',
                            url(r'^csv$',
                            self.__wrap(self.export_csv),
                            name='%s_%s_csv' % info)
                        )

        return ret




    def has_view_permission(self, request, obj=None):
        """
        Returns True if the given request has permission to change the given
        Django model instance.

        If `obj` is None, this should return True if the given request has
        permission to change *any* object of the given type.
        """
        opts = self.opts
        return request.user.has_perm(opts.app_label + '.' + opts.get_view_permission())

    @classmethod
    def _wrap(cls, f, field_name, label=None, order=None):
        """ utility to add extra attributes to method that could be used as columns """
        f.short_description = label or field_name
        f.allow_tags = True
        f.admin_order_field = order
        return f


    @classmethod
    def get_model_change_href(cls, field_name, label=None, order=None):

        def inner(self, obj):
            model_instance = getattr(obj, field_name)
            return self._get_model_change_url(model_instance) #IGNORE:W0212
        return cls._wrap(inner, field_name, label, order)

    @classmethod
    def get_email_href(cls, field_name, label=None, order=None):
        def inner(cls, obj):
            value = getattr(obj, field_name)
            if not value:
                return ""
            lbl = label or str(value)
            return "<a href='mailto:%s'>%s</a>" % (value, lbl)

        return cls._wrap(inner, field_name, label, order)

    def export_csv(self, request):
        from django.contrib.admin.views.main import ChangeList
        from django.utils.encoding import force_unicode
        import csv
        list_display = list(self.list_display)
        try:
            list_display.remove('action_checkbox')
        except ValueError:
            pass

        cl = ChangeList(request, self.model, list_display, (), (), None, (), False, 99999999, (), self)
        response = HttpResponse("", content_type='text/plain')
        name = force_unicode(self.verbose_name_plural)
        response["Content-type"] = "application/vnd.ms-excel"
        response["Content-Disposition"] = "attachment; filename=%s.csv" % str(name).replace(" ", "_")

        writer = csv.writer(response, delimiter=';', quotechar='"', quoting=csv.QUOTE_ALL)

        writer.writerow(list_display)

        for row in cl.result_list:
            line = []
            for field_name in list_display:
                try:
                    f = cl.lookup_opts.get_field(field_name)
                except models.FieldDoesNotExist:
                    if callable(field_name):
                        attr = field_name
                        value = attr(row)
                    elif hasattr(cl.model_admin, field_name) and \
                       not field_name == '__str__' and not field_name == '__unicode__':
                        attr = getattr(cl.model_admin, field_name)
                        value = attr(row)
                    else:
                        attr = getattr(row, field_name)
                        if callable(attr):
                            value = attr()
                        else:
                            value = attr

                else:
                    value = getattr(row, f.attname)
                try:
                    if value :
                        line.append(unicode.encode(unicode(value), 'utf-8'))
                    else:
                        line.append("")
                except UnicodeDecodeError, e:
                    raise Exception(e)
            writer.writerow(line)

        return response

    export_csv.label = _('Export csv')
    export_csv.css = "ui-icon-note"
    
ModelAdmin = XModelAdmin
site = admin.site    
